module Scribble

/// point - int or float?
type point = int * int

/// graphic mode
type Mode =
  | SelectMode // Selecting graphic element. On this version, do nothing
  | LineMode
  | RectMode
  | CircleMode
  | EllipseMode
  | OpenPolygonMode
  | ClosePolygonMode

/// user interaction
type UserAction =
  | LeftClick of point
  | RightClick of point
  | EscKey

/// graphic element
type GElement =
  | Line of point * point  // start, finish point
  | Rect of point * point  // upper left, lower right point
  | Circle of point * float  // center, radius
  | Ellipse of point * point  // enclosing box
  | OpenPolygon of point list
  | ClosePolygon of point list

type Rubber =
  | LineRubber
  | RectRubber

let (+=) ls x =
  ls := x :: !ls


type IView =
  abstract Refresh: unit -> unit

let notifyToAll (views: IView list ref) =
  !views |> List.iter (fun (v: IView) -> v.Refresh ())

type Model() =
  let views = ref ([]: IView list)
  let data = ref ([]: GElement list)

  member this.Register v = views += v
  member this.Data = !data

  /// notify(refresh) to all registered views
  member this.Notify () = notifyToAll views

  member this.Add (g: GElement) =
    data += g
    this.Notify ()

let consoleView =
  { new IView with
      member this.Refresh () = printfn "Console view"
  }

type GraphicView (c: Controller) =
  let cntl = c
  member this.SetRubber (r: Rubber) = ()
  interface IView with
    member this.Refresh () = printfn "Graphic view"

and Controller (v: GraphicView, m: Model) =
  let view = v
  let model = m
  member this.SetMode (m: Mode) = ()
  member this.Action (action: UserAction) = ()

let main =
  let m = new Model()
  m.Register consoleView
  m.Notify ()
